﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	/// <summary>
	/// This class represents each container type (array, list, set) that appears in the program.
	/// </summary>
	public class IRContainerType : IRClassType
	{
		/// <summary>
		/// True if the container is an array.
		/// </summary>
		private readonly bool m_isArray;

		/// <summary>
		/// True if the container is a list.
		/// </summary>
		private readonly bool m_isList;

		/// <summary>
		/// True if the container has set semantic behavior (e.g. does not have duplicates wrt. the equality predicate).
		/// </summary>
		private readonly bool m_hasSetSemantics;

		/// <summary>
		/// The type of the contents stored in this container
		/// </summary>
		private readonly IRType m_contentsType;

		/// <summary>
		/// The field that holds all the contents of this container
		/// </summary>
		private IRMemberField m_contentsfield;

		/// <summary>
		/// The field that holds all the hashes for the contents of this container (only on sets and dictionaries)
		/// </summary>
		private IRMemberField m_opthashfield;

		/// <summary>
		/// The field that holds that cardinality of the contianer
		/// </summary>
		private IRMemberField m_cardfield;

		/// <summary>
		/// The field that holds the capacity of the contianer
		/// </summary>
		private IRMemberField m_optcapacityfield;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{ Contract.Invariant(this.m_contentsType != null); }

		public IRContainerType(string tnamespace, string tbasename, TypeQualifiers tqual, IRType contentstype, bool isArray, bool isList, bool isSet)
			: base(tnamespace, tbasename, tqual)
		{
			Contract.Requires(!String.IsNullOrEmpty(tbasename));
			Contract.Requires(contentstype != null);

			this.m_contentsType = contentstype;
			this.m_isArray = isArray;
			this.m_isList = isList;
			this.m_hasSetSemantics = isSet;
		}

		/// <summary>
		/// Return true if the container is an array.
		/// </summary>
		public bool IsArrayContainer { get { return this.m_isArray; } }

		/// <summary>
		/// Return true if the container is a list.
		/// </summary>
		public bool IsListContainer { get { return this.m_isList; } }

		/// <summary>
		/// Return true if the container has set semantics wrt. the stored contents (note dictionaries have this property).
		/// </summary>
		public bool IsSetBehaviorContainer { get { return this.m_hasSetSemantics; } }

		/// <summary>
		/// Return the type of objects stored in the container.
		/// </summary>
		public IRType ContainerContentsType { get { return this.m_contentsType; } }

		/// <summary>
		/// The field that holds that cardinality of the contianer
		/// </summary>
		public IRMemberField CardField
		{ get { return this.m_cardfield; } }

		/// <summary>
		/// The field that holds the capacity this container (only on sets and dictionaries)
		/// </summary>
		public IRMemberField OptCapacityField
		{ get { return this.m_optcapacityfield; } }

		/// <summary>
		/// The field that holds all the contents of this container
		/// </summary>
		public IRMemberField ContentsField
		{ get { return this.m_contentsfield; } }

		/// <summary>
		/// The field that holds all the hashes for the contents of this container (only on sets and dictionaries)
		/// </summary>
		public IRMemberField OptHashField
		{ get { return this.m_opthashfield; } }

		public override void ProcessBuiltinInfoAsNeeded()
		{
			this.m_cardfield = this.TypeDescription.AllDefInFields.First((IRMemberField f) => f.FieldName.Equals("card"));
			this.m_optcapacityfield = this.TypeDescription.AllDefInFields.FirstOrDefault((IRMemberField f) => f.FieldName.Equals("capacity"));
			this.m_contentsfield = this.TypeDescription.AllDefInFields.First((IRMemberField f) => f.FieldName.Equals("allf"));
			this.m_opthashfield = this.TypeDescription.AllDefInFields.FirstOrDefault((IRMemberField f) => f.FieldName.Equals("allh"));
		}
	}

	/// <summary>
	/// This class represents objects of type dictionary
	/// </summary>
	public sealed class IRDictionaryType : IRContainerType
	{
		/// <summary>
		/// This field is the key value types that are stored in the dictionary
		/// </summary>
		private readonly IRKeyValuePairType m_kvptype;

		public IRDictionaryType(string tnamespace, string tbasename, TypeQualifiers tqual, IRKeyValuePairType kvpty)
			: base(tnamespace, tbasename, tqual, kvpty, false, false, true)
		{
			Contract.Requires(!String.IsNullOrEmpty(tbasename));
			Contract.Requires(kvpty != null);

			this.m_kvptype = kvpty;
		}

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{ Contract.Invariant(this.m_kvptype != null); }
	}
}
